/*********************************************************************
 * Copyright (C) 2004 Andrew Khan
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package jxl.biff.drawing;

import jxl.common.Assert;
import jxl.common.Logger;

import java.util.ArrayList;

/**
 * Class used to concatenate all the data for the various drawing objects
 * into one continuous stream
 */
public class DrawingData implements EscherStream {
    /**
     * The logger
     */
    private static Logger logger = Logger.getLogger(DrawingData.class);

    /**
     * The drawing data
     */
    private byte[] drawingData;

    /**
     * The number of drawings
     */
    private int numDrawings;

    /**
     * Initialized flag
     */
    private boolean initialized;

    /**
     * The spgr container. The contains the SpContainer for each drawing
     */
    private EscherRecord[] spContainers;

    /**
     * Constructor
     */
    public DrawingData() {
        numDrawings = 0;
        drawingData = null;
        initialized = false;
    }

    /**
     * Initialization
     */
    private void initialize() {
        EscherRecordData er = new EscherRecordData(this, 0);
        Assert.verify(er.isContainer());

        EscherContainer dgContainer = new EscherContainer(er);
        EscherRecord[] children = dgContainer.getChildren();

        children = dgContainer.getChildren();
        // Dg dg = (Dg) children[0];

        EscherContainer spgrContainer = null;

        for (int i = 0; i < children.length && spgrContainer == null; i++) {
            EscherRecord child = children[i];
            if (child.getType() == EscherRecordType.SPGR_CONTAINER) {
                spgrContainer = (EscherContainer) child;
            }
        }
        Assert.verify(spgrContainer != null);

        EscherRecord[] spgrChildren = spgrContainer.getChildren();

        // See if any of the spgrChildren are SpgrContainer
        boolean nestedContainers = false;
        for (int i = 0; i < spgrChildren.length && !nestedContainers; i++) {
            if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) {
                nestedContainers = true;
            }
        }

        // If there are no nested containers, simply set the spContainer list
        // to be the list of children
        if (!nestedContainers) {
            spContainers = spgrChildren;
        } else {
            // Go through the hierarchy and dig out all the Sp containers
            ArrayList sps = new ArrayList();
            getSpContainers(spgrContainer, sps);
            spContainers = new EscherRecord[sps.size()];
            spContainers = (EscherRecord[]) sps.toArray(spContainers);
        }

        initialized = true;
    }

    /**
     * Gets the sp container from the internal data
     *
     * @param spgrContainer the spgr container
     * @param sps the list of sp records
     */
    private void getSpContainers(EscherContainer spgrContainer, ArrayList sps) {
        EscherRecord[] spgrChildren = spgrContainer.getChildren();
        for (int i = 0; i < spgrChildren.length; i++) {
            if (spgrChildren[i].getType() == EscherRecordType.SP_CONTAINER) {
                sps.add(spgrChildren[i]);
            } else if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) {
                getSpContainers((EscherContainer) spgrChildren[i], sps);
            } else {
                logger.warn("Spgr Containers contains a record other than Sp/Spgr " +
                        "containers");
            }
        }
    }

    /**
     * Adds the byte stream to the drawing data
     *
     * @param data the data to add
     */
    public void addData(byte[] data) {
        addRawData(data);
        numDrawings++;
    }

    /**
     * Adds the data to the array without incrementing the drawing number.
     * This is used by comments, which for some bizarre and inexplicable
     * reason split out the data
     *
     * @param data the data to add
     */
    public void addRawData(byte[] data) {
        if (drawingData == null) {
            drawingData = data;
            return;
        }

        // Resize the array
        byte[] newArray = new byte[drawingData.length + data.length];
        System.arraycopy(drawingData, 0, newArray, 0, drawingData.length);
        System.arraycopy(data, 0, newArray, drawingData.length, data.length);
        drawingData = newArray;

        // Dirty up this object
        initialized = false;
    }

    /**
     * Accessor for the number of drawings
     *
     * @return the current count of drawings
     */
    final int getNumDrawings() {
        return numDrawings;
    }

    /**
     * Gets the sp container for the specified drawing number
     *
     * @param drawingNum the drawing number for which to return the spContainer
     * @return the spcontainer
     */
    EscherContainer getSpContainer(int drawingNum) {
        if (!initialized) {
            initialize();
        }

        if ((drawingNum + 1) >= spContainers.length) {
            throw new DrawingDataException();
        }

        EscherContainer spContainer =
                (EscherContainer) spContainers[drawingNum + 1];

        Assert.verify(spContainer != null);

        return spContainer;
    }

    /**
     * Gets the data which was read in for the drawings
     *
     * @return the drawing data
     */
    public byte[] getData() {
        return drawingData;
    }
}
